#ifndef __VECTOR_H
#define __VECTOR_H

#include <math.h>

/*#define PI      (3.14159265359f)*/
#define DEG2RAD(a)  (PI/180*(a))
#define RAD2DEG(a)  (180/PI*(a))

#define   acosl(x)         ((long   double)acos((double)(x)))
#define   cosf(x)           ((float)cos((double)(x)))
#define   sinf(x)           ((float)sin((double)(x)))
#define   acosf(x)         ((float)acos((double)(x)))


typedef float scalar_t;

class CVector
{
public:
    union
    {
        struct
        {
            scalar_t x;
            scalar_t y;
            scalar_t z;    // x,y,z coordinates
        };
        scalar_t v[3];
    };

public:
    CVector(scalar_t a = 0, scalar_t b = 0, scalar_t c = 0) : x(a), y(b), z(c) {}
    CVector(const CVector& vec) : x(vec.x), y(vec.y), z(vec.z) {}

    // vector index
    scalar_t& operator[](const long idx)
    {
        return *((&x) + idx);
    }

    // vector assignment
    const CVector& operator=(const CVector& vec)
    {
        x = vec.x;
        y = vec.y;
        z = vec.z;

        return *this;
    }

    // vecector equality
    const bool operator==(const CVector& vec) const
    {
        return ((x == vec.x) && (y == vec.y) && (z == vec.z));
    }

    // vecector inequality
    const bool operator!=(const CVector& vec) const
    {
        return !(*this == vec);
    }

    // vector add
    const CVector operator+(const CVector& vec) const
    {
        return CVector(x + vec.x, y + vec.y, z + vec.z);
    }

    // vector add (opposite of negation)
    const CVector operator+() const
    {
        return CVector(*this);
    }

    // vector increment
    const CVector& operator+=(const CVector& vec)
    {
        x += vec.x;
        y += vec.y;
        z += vec.z;
        return *this;
    }

    // vector subtraction
    const CVector operator-(const CVector& vec) const
    {
        return CVector(x - vec.x, y - vec.y, z - vec.z);
    }

    // vector negation
    const CVector operator-() const
    {
        return CVector(-x, -y, -z);
    }

    // vector decrement
    const CVector& operator-=(const CVector& vec)
    {
        x -= vec.x;
        y -= vec.y;
        z -= vec.z;

        return *this;
    }

    // scalar self-multiply
    const CVector& operator*=(const scalar_t& s)
    {
        x *= s;
        y *= s;
        z *= s;

        return *this;
    }

    // scalar self-divecide
    const CVector& operator/=(const scalar_t& s)
    {
        const float recip = 1 / s; // for speed, one divecision

        x *= recip;
        y *= recip;
        z *= recip;

        return *this;
    }

    // post multiply by scalar
    const CVector operator*(const scalar_t& s) const
    {
        return CVector(x * s, y * s, z * s);
    }

    // pre multiply by scalar
    friend inline const CVector operator*(const scalar_t& s, const CVector& vec)
    {
        return vec * s;
    }

    const CVector operator*(const CVector& vec) const
    {
        return CVector(x * vec.x, y * vec.y, z * vec.z);
    }

    // post multiply by scalar
    /*friend inline const CVector operator*(const CVector &vec, const scalar_t &s)
    {
         return CVector(vec.x*s, vec.y*s, vec.z*s);
    }*/

    // divide by scalar
    const CVector operator/(scalar_t s) const
    {
        s = 1 / s;

        return CVector(s * x, s * y, s * z);
    }

    // cross product
    const CVector CrossProduct(const CVector& vec) const
    {
        return CVector(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
    }

    // cross product
    const CVector operator^(const CVector& vec) const
    {
        return CVector(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
    }

    // dot product
    const scalar_t DotProduct(const CVector& vec) const
    {
        return x * vec.x + y * vec.x + z * vec.z;
    }

    // dot product
    const scalar_t operator%(const CVector& vec) const
    {
        return x * vec.x + y * vec.x + z * vec.z;
    }

    // length of vector
    const scalar_t Length() const
    {
        return (scalar_t)sqrt((double)(x * x + y * y + z * z));
    }

    // return the unit vector
    const CVector UnitVector() const
    {
        return (*this) / Length();
    }

    // normalize this vector
    void Normalize()
    {
        (*this) /= Length();
    }

    const scalar_t operator!() const
    {
        return sqrtf(x * x + y * y + z * z);
    }

    // return vector with specified length
    const CVector operator | (const scalar_t length) const
    {
        return *this * (length / !(*this));
    }

    // set length of vector equal to length
    const CVector& operator |= (const float length)
    {
        return *this = *this | length;
    }

    // return angle between two vectors
    const float inline Angle(const CVector& normal) const
    {
        return acosf(*this % normal);
    }

    // reflect this vector off surface with normal vector
    const CVector inline Reflection(const CVector& normal) const
    {
        const CVector vec(*this | 1);     // normalize this vector
        return (vec - normal * 2.0 * (vec % normal)) * !*this;
    }

    // rotate angle degrees about a normal
    const CVector inline Rotate(const float angle, const CVector& normal) const
    {
        const float cosine = cosf(angle);
        const float sine = sinf(angle);

        return CVector(*this * cosine + ((normal * *this) * (1.0f - cosine)) *
                       normal + (*this ^ normal) * sine);
    }
};

#endif